package backend1;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.security.InvalidParameterException;


/*
*
1. 当properties参数为空数组时，throw java.security.InvalidParameterException 异常。
*
2. source和target Object 可能是不同类型的class，但他们存在同名同类型的属性，就能进行拷贝。
*
3. 当properties参数中的属性，在source和target中同名但类型不一致时，throw RuntimeException 异常。
*
4. source Object的属性，如果是数组、对象等，需要进行深拷贝，即修改target的值不会同步修改source的值。
*
5. 只能使用 java.lang.reflect包下面的类或函数实现，不能使用第三方的库和函数，包括spring boot的。
*
6. 请补充各种异常情况和正常情况的测试代码，能处理循环引用等复杂情况者更佳。
*
* */
public class ObjectUtil {

    public static void main(String[] args) {
        //创建source和target对象
        source source = new source();
        target target = new target();

        //在source对象中赋值
        source.setName("xyh");
        source.setAge(21);
        source.setAddress(new address("四川"));

        //通过ObjectUtil工具类中的copyProperties方法对target对象赋值
        ObjectUtil.copyProperties(source, target, new String[]{"name", "age", "address"});

        //输出target对象的属性
        System.out.println(target);

    }

    //当properties参数为空数组时，throw java.security.InvalidParameterException 异常。
    public static void copyProperties(Object source, Object target, String[] properties) {
        if (properties == null){
            throw new InvalidParameterException();
        }

        //使用增强for遍历properties参数
        for (String property : properties) {
            try {
                //获取source对象和target对象的属性
                Field objectSourceField = source.getClass().getDeclaredField(property);
                Field objectTargetField = target.getClass().getDeclaredField(property);

                //当properties参数中的属性，在source和target中同名但类型不一致时，throw RuntimeException 异常。
                if (!(objectTargetField.getType().equals(objectSourceField.getType()))) {
                    throw new RuntimeException();
                }

                if (objectSourceField.getType().isArray()) {//判断属性是否为数组

                    //深拷贝，使用方法copyArrayOrObject将source对象中的某个字段的值复制到target对象的相应字段中
                    copyArray(objectSourceField.get(source), objectTargetField, target);

                } else if (objectSourceField.getType().isAssignableFrom(Object.class)) {//判断属性是否为对象

                    //获取source对象和target对象中的对象
                    Object sourceValue = (Object) objectSourceField.get(source);
                    Object targetValue = (Object) objectTargetField.get(target);

                    //递归调用copyProperties方法，直至将对象拆分完毕
                    copyProperties(sourceValue, targetValue, new String[]{property});

                } else {
                    //浅拷贝，获取source对象中的某个字段的值并复制到target对象的相应字段中
                    objectTargetField.set(target, objectSourceField.get(source));
                }

            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void copyArray(Object source, Field targetField, Object target){

        //如果source为null，将目标对象target中对应的字段也设置为null
        if (source == null) {
            try {
                targetField.set(target, null);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
            return;
        }

        //获取数组的元素类型
        Class<?> arrayType = source.getClass().getComponentType();
        //获取数组的长度
        int length = Array.getLength(source);

        //如果数组的元素类型是数组，递归调用copyArray方法
        if (arrayType.isArray()) {
            for (int i = 0; i < length; i++) {
                Object element = Array.get(source, i);
                copyArray(element, targetField, target);
            }
            return;
        }

        //创建一个与source具有相同元素类型的新数组
        Object newArray = Array.newInstance(arrayType, length);

        //遍历source数组，将source数组中的元素复制到新数组中
        for (int i = 0; i < length; i++) {
            Object element = Array.get(source, i);

            //如果元素不为 null 且是 Object 类型，则创建一个新的单元素数组，并调用 copyProperties 方法来复制元素的属性
            if (element != null && element instanceof Object) {
                Object newElement = Array.newInstance(element.getClass(), 1);
                copyProperties(element, newElement, new String[]{"value"});
                Array.set(newArray, i, newElement);
            } else {
                //直接将元素复制到新数组中
                Array.set(newArray, i, element);
            }
        }

        //将新数组赋值给目标对象
        try {
            targetField.set(target, newArray);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }


}
